package com.topc.code.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.topc.base.exception.Asserts;
import com.topc.code.judge.CodeSandbox;
import com.topc.code.judge.JudgeService;
import com.topc.code.judge.strategy.JudgeContext;
import com.topc.code.service.ICodeRunService;
import com.topc.code.service.IProblemContentCommitService;
import com.topc.code.service.IProblemInfoService;
import com.topc.code.service.IProblemUserMidService;
import com.topc.code.service.feign.CompetitionClient;
import com.topc.code.utils.SecurityUtil;
import com.topc.problem.model.code.CommitCodeResult;
import com.topc.problem.model.code.ExecuteCodeResponse;
import com.topc.problem.model.code.JudgeCompileInfo;
import com.topc.problem.model.code.JudgeInfo;
import com.topc.problem.model.dto.SaveProblemCommitDto;
import com.topc.problem.model.enums.JudgeInfoMessageEnum;
import com.topc.problem.model.enums.LanguageTypeEnum;
import com.topc.problem.model.enums.QuestionSubmitStatusEnum;
import com.topc.problem.model.po.ProblemContentCommit;
import com.topc.problem.model.po.ProblemInfo;
import com.topc.problem.model.po.ProblemUserMid;
import com.topc.problem.model.vo.CompetitionCheckInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;

@Service
public class CodeRunServiceImpl implements ICodeRunService {

    @Autowired
    private CodeSandbox codeSandbox;

    @Autowired
    private JudgeService judgeService;

    @Autowired
    private IProblemContentCommitService problemContentCommitService;

    @Autowired
    private IProblemInfoService problemInfoService;

    @Autowired
    private IProblemUserMidService problemUserMidService;

    @Autowired
    private CompetitionClient competitionClient;

    @Override
    public ExecuteCodeResponse codeDebug(JudgeCompileInfo judgeCompileInfo) {
        // 获取基本信息
        String language = judgeCompileInfo.getLanguage();
        // 传入信息检查
        checkLanguage(language);

        // 使用 CompletableFuture.supplyAsync 以便在异步操作完成后返回结果
        CompletableFuture<ExecuteCodeResponse> futureResponse = CompletableFuture.supplyAsync(() -> {
            ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
            AtomicReference<JudgeContext> judgeContext = new AtomicReference<>(new JudgeContext());

            // 执行判题
            judgeContext.set(judgeService.doDebug(judgeCompileInfo));

            // 保存信息
            executeCodeResponse.setOutputList(judgeContext.get().getOutputList());
            executeCodeResponse.setMessage("DEBUG SUCCESS");
            executeCodeResponse.setJudgeInfo(judgeContext.get().getJudgeInfo());

            return executeCodeResponse;
        });

        try {
            // 等待异步操作完成并返回结果
            return futureResponse.get();
        } catch (Exception e) {
            // 处理异常情况
            e.printStackTrace();
            // 根据需要返回一个错误的 ExecuteCodeResponse 对象或抛出异常
            ExecuteCodeResponse errorResponse = new ExecuteCodeResponse();
            errorResponse.setMessage("An error occurred during compilation.");
            return errorResponse;
        }
    }

    @Override
    public Long codeRun(SaveProblemCommitDto saveProblemCommitDto) {
        //获取当前时间(北京)
        LocalDateTime nowTime = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        //获取基本信息
        String code = saveProblemCommitDto.getCode();
        Long problemId = saveProblemCommitDto.getProblemId();
        Long competitionId= saveProblemCommitDto.getCompetitionId();
        System.out.println("==============>>>>>>>>>>>>"+competitionId);
        //TODO 判断题目是否存在于题组内 competitionId获取题组ID，题组ID获取题组列表，题组列表获取题目列表，检索题目列表
        String language = saveProblemCommitDto.getLanguage();
        String userNumber = SecurityUtil.getUser().getNumber();
        //传入信息检查
        //(竞赛id不为0的情况下)
        //通过feign获取竞赛时间
        CompetitionCheckInfoVo competitionCheckInfo = competitionClient.getCompetitionCheckInfo(competitionId, userNumber);
        if (ObjectUtils.isEmpty(competitionCheckInfo)){
            competitionCheckInfo=new CompetitionCheckInfoVo();
        }
        System.out.println("?????????????????????????????????"+competitionCheckInfo);
        competitionCheckInfo.setCompetitionId(competitionId);
        if (competitionId!=0){
            if (ObjectUtils.isEmpty(competitionCheckInfo)){
                Asserts.fail("竞赛不存在");
            }
            LocalDateTime startTime = competitionCheckInfo.getStartTime();
            LocalDateTime endTime = competitionCheckInfo.getEndTime();
            Integer status = competitionCheckInfo.getStatus();
            //如果比赛没结束，检查状态
            if(!nowTime.isAfter(endTime)){
                if (ObjectUtils.isEmpty(competitionCheckInfo.getStatus())){
                    Asserts.fail("未查询到报名信息");
                }
                //时间检查
                checkTimeBefore(startTime,nowTime);
                //报名状态检查(已提交/禁赛)
                checkStatus(status);
            }
            competitionCheckInfo.setNowTime(nowTime);
        }
        //语言检查
        checkLanguage(language);
        //问题ID检查
        checkProblemInfo(problemId);
        //是否正在判题
        checkCommitInfo(problemId, userNumber,competitionId);
        //1.保存题目提交记录
        //保存语言编号、程序内容以及题目id
        ProblemContentCommit problemContentCommit = new ProblemContentCommit();
        Integer codeType = LanguageTypeEnum.getEnumByValue(language).getText();
        problemContentCommit.setCodeType(codeType);
        problemContentCommit.setContent(code);
        boolean saveContent = problemContentCommitService.save(problemContentCommit);
        if (!saveContent){
            Asserts.fail("提交内容保存失败");
        }
        //新建情况信息
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setMessage(JudgeInfoMessageEnum.getEnumByValue("等待中").getText());
        judgeInfo.setMemory(BigDecimal.valueOf(1000L));
        judgeInfo.setTime(BigDecimal.valueOf(1000L));
        Long contentId = problemContentCommit.getId();
        ProblemUserMid problemUserMid = new ProblemUserMid();
        problemUserMid.setStatus(QuestionSubmitStatusEnum.WAITING.getValue());
        problemUserMid.setProblemId(problemId);
        problemUserMid.setUserNumber(userNumber);
        problemUserMid.setContentId(contentId);
        problemUserMid.setConditionMid(JSON.toJSONString(judgeInfo));
        problemUserMid.setCompetitionId(competitionId);
        boolean saveInfo = problemUserMidService.save(problemUserMid);
        if (!saveInfo){
            Asserts.fail("保存失败");
        }
        //查询题目是否提交完成，输入题目提交记录的id，获取题目提交的代码以及语言
        Long problemUserMidId = problemUserMid.getId();
        //异步操作，执行判题流程
        CompetitionCheckInfoVo finalCompetitionCheckInfo = competitionCheckInfo;
        CompletableFuture.runAsync(() -> {
            judgeService.doJudge(problemUserMidId, finalCompetitionCheckInfo);
        });
        //返回提交记录的id
        return problemUserMidId;
    }


    @Override
    public CommitCodeResult getCodeResponse(Long problemUserMidId) {
        ProblemUserMid problemUserMidResult = problemUserMidService.getById(problemUserMidId);
        if (problemUserMidResult == null){
            Asserts.fail("提交代码异常");
        }
        if (!Objects.equals(problemUserMidResult.getUserNumber(), Objects.requireNonNull(SecurityUtil.getUser()).getNumber())){
            Asserts.fail("没有查看权限");
        }
        CommitCodeResult commitCodeResult = new CommitCodeResult();
        String conditionMid = problemUserMidResult.getConditionMid();
        JudgeInfo judgeInfoResult = JSONUtil.toBean(conditionMid, JudgeInfo.class);
        commitCodeResult.setMessage(QuestionSubmitStatusEnum.getEnumByValue(problemUserMidResult.getStatus()).getText());
        commitCodeResult.setJudgeInfo(judgeInfoResult);
        commitCodeResult.setScore(problemUserMidResult.getScore());
        return commitCodeResult;
    }
    //一次性获取结果(无用)
//    public CommitCodeResult getRunResult(SaveProblemCommitDto saveProblemCommitDto){
//        Long l = codeRun(saveProblemCommitDto);
//        CommitCodeResult codeResponse = getCodeResponse(l);
//        return codeResponse;
//    }
    private void checkCommitInfo(Long problemId, String userNumber,Long competitionId) {
        //判断这条信息用户是否已经提交过,提交状态为0并且是该用户和该题目对应的userNumber和id
        boolean existed=problemUserMidService.ifExistInfo(problemId, userNumber,competitionId);
        if (existed){
            Asserts.fail("正在判题中，请稍后提交");
        }
    }

    private void checkProblemInfo(Long problemId) {
        //判断问题是否存在
        ProblemInfo ifProblemExist = problemInfoService.getById(problemId);
        if (ifProblemExist==null){
            Asserts.fail("问题不存在");
        }
    }

    //判断语言是否合法
    private void checkLanguage(String language) {
        LanguageTypeEnum enumByValue = LanguageTypeEnum.getEnumByValue(language);
        if (enumByValue==null){
            Asserts.fail("语言不合法");
        }
    }

    //判断是否在比赛开始前
    private void checkTimeBefore(LocalDateTime startTime, LocalDateTime nowTime) {
        //判断当前时间是否在开始时间之后
        if (nowTime.isBefore(startTime)){
            Asserts.fail("比赛未开始");
        }
    }

    //检查竞赛状态(已提交/禁赛)
    private void checkStatus(Integer status){
        if (status==-1){
            Asserts.fail("用户已被禁赛");
        }
        if (status==1){
            Asserts.fail("用户已交卷");
        }
    }
}
